﻿using PredictionBuilder.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace PredictionBuilder.Attributes
{
    /// <summary>
    /// 自定义方法特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class CustomMethodAttribute : Attribute
    {
        /// <summary>
        /// 方法所在的类
        /// </summary>
        public Type MethodType { get; private set; }
        /// <summary>
        /// 需要调用的静态方法名称
        /// </summary>

        public string MethodName { get; private set; }
        /// <summary>
        /// 方法结果比较符
        /// </summary>
        public OperatorType OperatorType { get; private set; }
        /// <summary>
        /// 方法返回结果的标准值
        /// </summary>
        public object MethodPredictValue { get; private set; }
        /// <summary>
        /// 自定义方法特性
        /// </summary>
        /// <param name="typeName">静态方法所在的类名</param>
        /// <param name="methodName">方法名</param>
        /// <param name="operatorType">操作符</param>
        /// <param name="methodPredictValue">方法返回预计值</param>
        public CustomMethodAttribute(string typeName, string methodName, OperatorType operatorType, object methodPredictValue)
        {
            GetType(typeName);
            MethodName = methodName;
            OperatorType = operatorType;
            MethodPredictValue = methodPredictValue;
        }
        /// <summary>
        /// 自定义方法特性
        /// </summary>
        /// <param name="methodType">静态方法所在类</param>
        /// <param name="methodName">静态类名</param>
        /// <param name="operatorType">操作符</param>
        /// <param name="methodPredictValue">预期返回值</param>
        public CustomMethodAttribute(Type methodType, string methodName, OperatorType operatorType, object methodPredictValue)
        {
            MethodType = methodType;
            MethodName = methodName;
            OperatorType = operatorType;
            MethodPredictValue = methodPredictValue;
        }
        private void GetType(string typeName)
        {
            var methodType = Type.GetType(typeName);
            if (methodType == null)
            {
                throw new ArgumentException($"can not find type: {typeName}");
            }
            else
            {
                MethodType = methodType;
            }
        }
        /// <summary>
        /// 获取自定义的方法
        /// </summary>
        /// <param name="selectorType">生成表达式的属性</param>
        /// <param name="value">查询实体获取的值</param>
        /// <param name="parameterOrderType">参数顺序</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public MethodInfo GetCustomMethod(Type selectorType, object value, ParameterOrderType parameterOrderType)
        {
            Type left;
            Type right;
            if (parameterOrderType == ParameterOrderType.Asc)
            {
                left = selectorType;
                right = value.GetType();
            }
            else
            {
                left = value.GetType();
                right = selectorType;
            }
            MethodInfo method = MethodType.GetMethods(BindingFlags.Public | BindingFlags.Static)
                .Where(t =>
                {
                    if (t.Name != MethodName || t.GetParameters().Length != 2 || t.GetGenericArguments().Length > 2 || t.ReturnType == typeof(void))
                    {
                        return false;
                    }
                    else
                    {
                        var pars = t.GetParameters();
                        bool flag = pars[0].ParameterType == typeof(object) ? true : pars[0].ParameterType == left || pars[0].ParameterType == left.BaseType;
                        bool flag1 = pars[1].ParameterType == typeof(object) ? true : pars[1].ParameterType == right.BaseType || pars[1].ParameterType == right;
                        if (!t.IsGenericMethod)
                        {
                            return flag && flag1;
                        }
                        else
                        {
                            return (pars[0].ParameterType.IsGenericParameter ? true : flag) && (pars[1].ParameterType.IsGenericParameter ? true : flag1);
                        }
                    }
                })
                .OrderByDescending(t => t.GetParameters().Length)
                .OrderBy(t => t.GetGenericArguments().Length)
                .FirstOrDefault();
            if (method == null)
            {
                throw new ArgumentException($"can not find method:{MethodName}!Please confirm if the method name is correct, the method parameters are two, and the maximum number of generic parameters is two");
            }
            //如果是泛型方法
            var parameters = method.GetParameters();
            if (method.IsGenericMethod)
            {
                List<(Type, bool)> genericArgs = new List<(Type, bool)>() { (left, false), (right, false) };
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (parameters[i].ParameterType.IsGenericParameter)
                    {
                        genericArgs[i] = (genericArgs[i].Item1, true);
                    }
                }
                return method.MakeGenericMethod(genericArgs.Where(t => t.Item2).Select(t => t.Item1).ToArray());
            }
            else
            {
                return method;
            }
        }
    }
}
